package gui.batches.command;

import hit.core.Item;

import java.util.ArrayList;
import java.util.List;

/**
 * An object that handles the history of Commands for a given controller
 * */
public class CommandHistory {
	//use lists to represent stacks
	private List<Command> executedCommands;
	private List<Command> undoneCommands;
	
	/**
	 * Initializes the CommandHistory
	 * */
	public CommandHistory() {
		executedCommands = new ArrayList<Command>();
		undoneCommands = new ArrayList<Command>();
	}
	
	/**
	 * Execute a command and add it to the history of Commands
	 * @throws IllegalArgumentException thrown if command is null
	 * */
	public void Execute(Command command) {
		if (command == null) {
			throw new IllegalArgumentException("Command cannot be null");
		}
		
		//execute the command and add it to the Stack of added commands
		command.Execute();
		executedCommands.add(0, command);
		undoneCommands.clear();
	}
	
	/**
	 * Determines whether or not the CommandHistory has Commands to undo
	 * @return whether or not can undo
	 * */
	public boolean CanUndo() {
		if (executedCommands.size() == 0) {
			return false;
		}
		return true;
	}
	
	/**
	 * Undo the last Command
	 * @return the List of Items affected by the undo operation
	 * @throws IllegalStateException thrown if there are no Commands to undo
	 * */
	public List<Item> Undo() throws IllegalStateException {
		if (executedCommands.size() == 0) {
			throw new IllegalStateException("No Commands to undo");
		}
		
		//undo the command, pop it off executed and push it onto undone
		Command command = executedCommands.get(0);
		executedCommands.remove(0);
		undoneCommands.add(0, command);
		return command.Undo();
	}
	
	/**
	 * Returns whether or not the CommandHistory has Commands to redo
	 * @return whether or not can redo
	 * */
	public boolean CanRedo() {
		if (undoneCommands.size() == 0)
			return false;
		return true;
	}
	
	/**
	 * Redo the last undone Command
	 * @return the List of Items affected by redoing the Command
	 * @throws IllegalStateException thrown if there are no Commands to redo
	 * */
	public List<Item> Redo() throws IllegalStateException {
		if (undoneCommands.size() == 0) {
			throw new IllegalStateException("No Commands to redo");
		}
		
		//redo the command, pop it off undone, and then push it onto executed
		Command command = undoneCommands.get(0);
		undoneCommands.remove(0);
		executedCommands.add(0, command);
		return command.Execute();
	}
}
